# -*- coding: utf-8 -*-
 
import sys
import threading
import time
from PySide6.QtCore import *
from PySide6.QtWidgets import QApplication, QWidget, QTextBrowser, QPushButton, QHBoxLayout, QVBoxLayout, QMessageBox
 
# 创建QApplication对象
if not QApplication.instance():
    app = QApplication([])
else:
    app = QApplication.instance()
 
 
def thread_ui(func, *args):
    """
    开启一个新线程任务\n
    :param func: 要执行的线程函数;
    :param args: 函数中需要传入的参数 Any
    :return:
    """
    t = threading.Thread(target=func, args=args)  # 定义新线程
    t.setDaemon(True)  # 开启线程守护
    t.start()  # 执行线程
 
 
class SignThread(QThread):
    """信号线程"""
 
    def __new__(cls, parent: QWidget, func, *types: type):
        cls.__update_date = Signal(*types, name=func.__name__)  # 定义信号(*types)一个信号中可以有一个或多个类型的数据(int,str,list,...)
        return super().__new__(cls)  # 使用父类__new__方法创建SignThread实例对象
 
    def __init__(self, parent: QWidget, func, *types: type):
        """
        信号线程初始化\n
        :param parent: 界面UI控件
        :param func: 信号要绑定的方法
        :param types: 信号类型,可以是一个或多个(type,...)
        """
        super().__init__(parent)  # 初始化父类
        self.__update_date.connect(func)  # 绑定信号与方法
 
    def send_sign(self, *args):
        """
        使用SignThread发送信号\n
        :param args: 信号的内容
        :return:
        """
        self.__update_date.emit(*args)  # 发送信号元组(type,...)
 
 
class MyWindow(QWidget):
    """UI界面"""
 
    def __init__(self):
        """UI界面初始化"""
        super().__init__()  # 初始化父级
        self.setWindowTitle('子线程更新UI线程实例')  # 设置窗口标题
        self.text = QTextBrowser()  # 定义文本浏览框
        self.button = QPushButton("添加文本")  # 定义发送信号按钮
        self.clear_button = QPushButton("清除文本")  # 定义清除文本按钮
        self.main_layout = QHBoxLayout(self)  # 定义水平布局容器
        self.son_layout = QVBoxLayout()  # 定义垂直布局容器
        self.main_layout.addWidget(self.text)  # 布局文本浏览框
        self.main_layout.addLayout(self.son_layout)  # 布局垂直布局容器
        self.son_layout.addWidget(self.button)  # 布局发送信号按钮
        self.son_layout.addWidget(self.clear_button)  # 布局清除文本按钮
        self.button.clicked.connect(self.button_click)  # 发送信号按钮点击事件绑定(button_click)函数
        self.clear_button.clicked.connect(self.clear_click)  # 清除文本按钮点击事件绑定(clear_click)函数
        self.add_thread = SignThread(self, self.add_text, str, int)  # 创建信号线程,并绑定add_text函数,信号使用str和int两种数据
        self.clear_thread = SignThread(self, self.clear_text, int)  # 创建信号线程,并绑定clear_text函数,信号只使用int数据
 
    def button_click(self):
        """点击发送信号按钮时执行此函数"""
        def button_thread():
            for i in range(5):
                self.add_thread.send_sign(f"添加倒计时：{5-i}", i)  # 发送信号(倒计时：{5-i}, i)
                time.sleep(1)  # 延时1秒(不管延时多久,UI界面都不会出现假死状态)
            self.add_thread.send_sign('你好啊', 99)  # 发送信号(你好啊, 99)
        return thread_ui(button_thread)  # 开启新线程来执行(button_thread)函数
 
    def clear_click(self):
        """点击清除文本按钮时执行此函数"""
        def clear_text():
            for i in range(5):
                self.clear_thread.send_sign(0)  # 发送信号(0)
                time.sleep(1)  # 延时1秒(不管延时多久,UI界面都不会出现假死状态)
            self.clear_thread.send_sign(1)  # 发送信号(1)
        return thread_ui(clear_text)  # 开启新线程来执行(clear_text)函数
 
    def add_text(self, *args):
        """
        收到add_thread信号时执行此函数\n
        :param args: 接收的信号元组
        :return:
        """
        self.text.append(f'str: {args[0]} int: {args[1]}')  # 在文本浏览框中追加内容(data)
 
    def clear_text(self, *args):
        """
        收到clear_thread信号时执行此函数\n
        :param args: 接收的信号元组
        :return: 
        """
        self.text.clear()  # 清除文本浏览框中的内容
        if args[0] == 1:  # 当信号元组的第一个元素为整型1时
            msg = QMessageBox(self)
            msg.setText(f"清除文本结束")
            msg.exec()
 
 
def main():
    widget = MyWindow()  # 实例化UI线程
    widget.resize(300, 200)  # 设置窗口大小
    widget.show()  # 展示窗口
    sys.exit(app.exec())  # 持续刷新窗口
 
 
if __name__ == '__main__':
    main()